package com.renfei.multimediatask.ui.task10.decodec;

import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.media.MediaRecorder;
import android.os.Build;
import android.util.Log;
import android.view.Surface;

import androidx.annotation.RequiresApi;

import com.renfei.multimediatask.ui.task10.VideoPlayActivity;
import com.renfei.multimediatask.ui.task7.ADTSUtils;

import java.io.IOException;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;

public class BaseDecodec {


    private String targetFilePath;
    private Surface outputSurface;


    private AudioDecodecThread audioDecodecThread;
    private VideoDecodecThread videoDecodecThread;

    @RequiresApi(api = Build.VERSION_CODES.Q)
    public BaseDecodec(String targetFilePath, Surface outputSurface) {
        this.targetFilePath = targetFilePath;
        this.outputSurface = outputSurface;

        initThread();
    }

    private void initThread() {
        audioDecodecThread = new AudioDecodecThread(new WeakReference<>(this));
        videoDecodecThread = new VideoDecodecThread(new WeakReference<>(this));
    }


    public void startPlay() {
        audioDecodecThread.start();
        videoDecodecThread.start();
    }

    public void stopPlay() {
        audioDecodecThread.stopPlay();
        videoDecodecThread.stopDecodec();

    }


    static class AudioDecodecThread extends Thread {
        //等待超时时间
        private static final int TIMEOUT_US = 1000;

        private String TAG = "AudioDecodecThread";
        private String audioType = "audio/";


        private AudioTrack audioTrack;

        private WeakReference<BaseDecodec> weakReference;

        private int streamType = AudioManager.STREAM_MUSIC;//

        private int sampleRateInHz = 44100;//
        private int channelConfig = AudioFormat.CHANNEL_OUT_STEREO;//
        private int audioSampleFormat = AudioFormat.ENCODING_PCM_16BIT;

        private int mode = AudioTrack.MODE_STREAM;

        private boolean isPlaying = false;


        private MediaCodec audioDecodec;
        private MediaCodec.BufferInfo audioBufferInfo;

        private MediaExtractor mediaExtractor;

        @RequiresApi(api = Build.VERSION_CODES.Q)
        public AudioDecodecThread(WeakReference<BaseDecodec> weakReference) {
            this.weakReference = weakReference;
            audioBufferInfo = new MediaCodec.BufferInfo();

            initMediaExectorInThread();
            initAudioTrack();

        }

        @RequiresApi(api = Build.VERSION_CODES.Q)
        private void initMediaExectorInThread() {
            try {
                mediaExtractor = new MediaExtractor();
                mediaExtractor.setDataSource(weakReference.get().targetFilePath);

                for (int i = 0; i < mediaExtractor.getTrackCount(); i++) {
                    MediaFormat format = mediaExtractor.getTrackFormat(i);

                    String mine = format.getString(MediaFormat.KEY_MIME);

                    if (mine.startsWith(audioType)) {
                        mediaExtractor.selectTrack(i);
                        sampleRateInHz = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
                        Log.e(TAG, "mineType: " + mine);

                        audioDecodec = MediaCodec.createDecoderByType(mine);
                        audioDecodec.configure(format, null, null, 0);
                        audioDecodec.start();
                    }
                }

            } catch (IOException e) {
                e.printStackTrace();
            }

        }

        private void initAudioTrack() {

            int bufferSizeInBytes = AudioTrack.getMinBufferSize(sampleRateInHz,
                    channelConfig,
                    audioSampleFormat);
            audioTrack = new AudioTrack(
                    streamType, sampleRateInHz, channelConfig, audioSampleFormat, bufferSizeInBytes, mode
            );
        }

        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        @Override
        public void run() {
            super.run();


            isPlaying = true;

            audioTrack.play();
            int sampleSize = 0;
            while (isPlaying) {
                //输入
                int inputBufferIndex = audioDecodec.dequeueInputBuffer(-1);
                if (inputBufferIndex >= 0) {
                    ByteBuffer inputBuffer = audioDecodec.getInputBuffer(inputBufferIndex);
                    inputBuffer.clear();
                    sampleSize = mediaExtractor.readSampleData(inputBuffer, 0);
                    Log.e(TAG, "mediaExtractor.readSampleData: " + sampleSize);
                    if (sampleSize > 0) {
                        long presentationTimeUs = mediaExtractor.getSampleTime();
                        audioDecodec.queueInputBuffer(inputBufferIndex, 0, sampleSize, presentationTimeUs, 0 /*flags*/);
                        mediaExtractor.advance();
                    } else {
                        isPlaying = false;
                        audioDecodec.queueInputBuffer(inputBufferIndex, 0, 0, 0L, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                    }
                } else if (inputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
                    Log.e(TAG, "等待输入缓冲区: ");
                }

                //输出

                int outputBufferIndex = audioDecodec.dequeueOutputBuffer(audioBufferInfo, TIMEOUT_US);

                while (outputBufferIndex >= 0) {
                    Log.e(TAG, "outputBufferIndex: " + outputBufferIndex);

                    ByteBuffer outputBuffer = audioDecodec.getOutputBuffer(outputBufferIndex);

                    final byte[] chunk = new byte[audioBufferInfo.size];
                    outputBuffer.get(chunk);

                    audioTrack.write(chunk, audioBufferInfo.offset, audioBufferInfo.offset + audioBufferInfo.size);

                    outputBuffer.clear();
                    audioDecodec.releaseOutputBuffer(outputBufferIndex, false);
                    outputBufferIndex = audioDecodec.dequeueOutputBuffer(audioBufferInfo, TIMEOUT_US);
                }

            }

            isPlaying = false;

            audioDecodec.stop();
            audioDecodec.release();

            audioTrack.stop();
            audioTrack.release();

            mediaExtractor.release();

            Log.e(TAG, ":音频播放结束 ");
        }

        public void stopPlay() {
            isPlaying = false;
        }
    }

    static class VideoDecodecThread extends Thread {
        private WeakReference<BaseDecodec> weakReference;
        private MediaCodec videoDecodec;
        private Surface outputSurface;
        private MediaCodec.BufferInfo videoBufferInfo;

        private MediaExtractor mediaExtractor;
        private int videoFrameRate=50;


        private boolean isDecodec = false;
        private String TAG = "VideoDecodecThread";
        private long TIMEOUT_USEC = 1000;

        private String videoType = "video/";


        @RequiresApi(api = Build.VERSION_CODES.Q)
        public VideoDecodecThread(WeakReference<BaseDecodec> weakReference) {
            this.weakReference = weakReference;
            this.outputSurface = weakReference.get().outputSurface;

            videoBufferInfo = new MediaCodec.BufferInfo();
            initMediaExectorInThread();
        }

        @RequiresApi(api = Build.VERSION_CODES.Q)
        private void initMediaExectorInThread() {

            try {
                mediaExtractor = new MediaExtractor();
                mediaExtractor.setDataSource(weakReference.get().targetFilePath);

                for (int i = 0; i < mediaExtractor.getTrackCount(); i++) {
                    MediaFormat format = mediaExtractor.getTrackFormat(i);

                    String mine = format.getString(MediaFormat.KEY_MIME);

                    if (mine.startsWith(videoType)) {
                        mediaExtractor.selectTrack(i);

                        //int width = format.getInteger(MediaFormat.KEY_WIDTH, 0);
                        //int height = format.getInteger(MediaFormat.KEY_HEIGHT, 0);
                        videoFrameRate = format.getInteger(MediaFormat.KEY_FRAME_RATE, 25);

                        videoDecodec = MediaCodec.createDecoderByType(mine);
                        videoDecodec.configure(format, outputSurface, null, 0);
                        videoDecodec.start();

                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }


        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        @Override
        public void run() {
            super.run();
            isDecodec = true;

            int sampleSize = 0;
            while (isDecodec) {


                  int inputBufferIndex = videoDecodec.dequeueInputBuffer(-1);
                  if (inputBufferIndex >= 0) {
                      ByteBuffer inputBuffer = videoDecodec.getInputBuffer(inputBufferIndex);
                      inputBuffer.clear();
                      sampleSize = mediaExtractor.readSampleData(inputBuffer, 0);
                      if (sampleSize > 0) {
                          long presentationTimeUs = mediaExtractor.getSampleTime();
                          videoDecodec.queueInputBuffer(inputBufferIndex, 0, sampleSize, presentationTimeUs, 0 /*flags*/);
                          mediaExtractor.advance();
                      } else {
                          isDecodec = false;
                          videoDecodec.queueInputBuffer(inputBufferIndex, 0, 0, 0L, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                      }
                  } else if (inputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
                      Log.e(TAG, "等待输入缓冲区: ");
                  }
//
                //输出

                int outputBufferIndex = videoDecodec.dequeueOutputBuffer(videoBufferInfo, TIMEOUT_USEC);
                while (outputBufferIndex >= 0) {
                    try {
                        sleep(1000 / videoFrameRate);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    videoDecodec.releaseOutputBuffer(outputBufferIndex, true);
                    outputBufferIndex = videoDecodec.dequeueOutputBuffer(videoBufferInfo, TIMEOUT_USEC);
                }
            }

            isDecodec = false;

            videoDecodec.stop();
            videoDecodec.release();

            mediaExtractor.release();
            Log.e(TAG, ":音频播放结束 ");

        }

        private void stopDecodec() {
            isDecodec = false;
        }


    }

}
